//-----------------------------------------------------------------------------
// File: CGameFramework.cpp
//-----------------------------------------------------------------------------

#include "stdafx.h"
#include "GameObject.h"
#include "Timer.h"
#include "Camera.h"
#include "Player.h"
#include "Scene.h"
#include "GameFramework.h"

#include "ClientObjectManager.h"

CGameFramework::CGameFramework()
{
    m_pD3D = NULL;        	  
	m_pD3DDevice = NULL;

	m_pScene = NULL;
    //m_pCamera = NULL;    
    //m_pPlayer = NULL;           

	m_hInstance = NULL;
	m_hWnd = NULL;     

	m_xViewport = 0;
	m_yViewport = 0;
	m_cxViewport = FULLSCREEN_WIDTH;
	m_cyViewport = FULLSCREEN_HEIGHT;

	m_bLostDevice = false;      
	m_bActive = true;    
	m_bWindowed = true;
}

CGameFramework::~CGameFramework()
{
}

bool CGameFramework::OnCreate(HINSTANCE hInstance, HWND hMainWnd)
{
	m_hInstance = hInstance;
	m_hWnd = hMainWnd;

	RECT rc;
	GetClientRect(m_hWnd, &rc);
	m_cxViewport = rc.right - rc.left;
	m_cyViewport = rc.bottom - rc.top;

    if (!(m_pD3D = Direct3DCreate9(D3D_SDK_VERSION))) return(false);

	if (!CreateDirect3DDisplay()) return(false); 

	if (!BuildObjects()) return(false); 

	SetupGameState();

	SetupRenderStates();

	return(true);
}

D3DPRESENT_PARAMETERS CGameFramework::BuildPresentParameters()
{
    D3DPRESENT_PARAMETERS d3dpp; 
	ZeroMemory(&d3dpp, sizeof(d3dpp));
	d3dpp.BackBufferCount = 1;
	d3dpp.BackBufferWidth = (m_bWindowed) ? m_cxViewport : FULLSCREEN_WIDTH;
	d3dpp.BackBufferHeight = (m_bWindowed) ? m_cyViewport : FULLSCREEN_HEIGHT;
	d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
	d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
	d3dpp.MultiSampleQuality = 0;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.hDeviceWindow = m_hWnd;
	d3dpp.Windowed = m_bWindowed;
    d3dpp.EnableAutoDepthStencil = TRUE;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
    d3dpp.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL | D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
	d3dpp.FullScreen_RefreshRateInHz = (m_bWindowed) ? 0 : 60; 
	d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
    return(d3dpp);
}

void CGameFramework::ChangeWindowProperties()
{
	if (m_bWindowed)
  	{
		UINT uFlags = SWP_NOACTIVATE | SWP_SHOWWINDOW;
        SetWindowLong(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
		RECT rc;
		GetWindowRect(m_hWnd, &rc);
		SetWindowPos(m_hWnd, HWND_NOTOPMOST, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, uFlags);
		if (m_hMenu) SetMenu(m_hWnd, m_hMenu);
	} 
 	else
	{
		m_hMenu = GetMenu(m_hWnd);
		SetMenu(m_hWnd, NULL);
		SetWindowLong(m_hWnd, GWL_STYLE, WS_VISIBLE | WS_POPUP);
		SetWindowPos(m_hWnd, NULL, 0, 0, m_cxViewport, m_cyViewport, SWP_NOZORDER);
	} 
}

bool CGameFramework::CreateDirect3DDisplay()
{
	if (m_hWnd) ChangeWindowProperties();

    if (m_pD3DDevice) m_pD3DDevice->Release();
    m_pD3DDevice = NULL;

	HRESULT hResult;
	D3DPRESENT_PARAMETERS d3dpp = BuildPresentParameters();
	if (FAILED(hResult = m_pD3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &m_pD3DDevice))) return(false);

	return(true);
}

void CGameFramework::ResetDisplay(bool bChangeDevice, int nClientWidth, int nClientHeight)
{   
    if (m_pD3DDevice) 
    {
		if (bChangeDevice) m_bWindowed = !m_bWindowed;
		if (m_bWindowed && (nClientWidth > 0) && (nClientHeight > 0))
		{
			m_cxViewport = nClientWidth;
			m_cyViewport = nClientHeight;
		}

		if (m_hWnd) ChangeWindowProperties();

		D3DPRESENT_PARAMETERS d3dpp = BuildPresentParameters();
		HRESULT hResult = m_pD3DDevice->Reset(&d3dpp);
        if (hResult == D3DERR_DEVICELOST) 
		{
			SetActive(false);
		}
		else
		{
			SetupRenderStates();
		}
	}
}

void CGameFramework::SetupGameState()
{
	BOUNDINGCUBE bcBoundingCube;
    bcBoundingCube.m_vMinimum = D3DXVECTOR3(-2.0f, -2.0f, -2.0f);
    bcBoundingCube.m_vMaximum = D3DXVECTOR3(+2.0f, +2.0f, +2.0f);
 //   m_pScene->GetPlayer()->SetBoundingCube(bcBoundingCube);

	m_pScene->OnChangeCamera(THIRD_PERSON_CAMERA, m_GameTimer.GetTimeElapsed());
	if (m_pScene->GetCamera())
	{
		m_pScene->GetCamera()->SetFOV(60.0f);
		m_pScene->GetCamera()->SetViewportToDevice(m_xViewport, m_yViewport, m_cxViewport, m_cyViewport, 1.01f, 5000.0f);
		m_pScene->GetCamera()->SetBoundingCube(bcBoundingCube);
	}

    m_pScene->GetPlayer(0)->SetPosition(D3DXVECTOR3(1000.0f, 300.0f, 1000.0f));
    m_pScene->GetPlayer(0)->Rotate(0.0f, 0.0f, 0.0f);	
}

void CGameFramework::SetupRenderStates()
{
    RECT rc;
    ::GetClientRect(m_hWnd, &rc);
    m_cxViewport = rc.right - rc.left;
    m_cyViewport = rc.bottom - rc.top;

	if (m_pScene->GetCamera())
	{
		m_pScene->GetCamera()->SetViewportToDevice(m_xViewport, m_yViewport, m_cxViewport, m_cyViewport, 1.01f, 50000.0f);
		m_pScene->GetCamera()->SetViewMatrixToDevice(m_pD3DDevice);
		m_pScene->GetCamera()->SetProjectionMatrixToDevice(m_pD3DDevice);
	}

    m_pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
    m_pD3DDevice->SetRenderState(D3DRS_DITHERENABLE, TRUE);
    m_pD3DDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
    m_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);

	m_pD3DDevice->SetRenderState(D3DRS_LIGHTING, TRUE);
	m_pD3DDevice->SetRenderState(D3DRS_AMBIENT, 0xff101010);

	m_pScene->OnLight();
}

//void CGameFramework::OnChangeCamera(ULONG nCameraMode)
//{
//    switch (nCameraMode)
//    {
//        case FIRST_PERSON_CAMERA:            
//            m_pPlayer->SetFriction(200.0f); 
//            m_pPlayer->SetGravity(D3DXVECTOR3(0.0f, 0.0f, 0.0f));
//            m_pPlayer->SetMaxVelocityXZ(80.0f);
//            m_pPlayer->SetMaxVelocityY(400.0f);
//            m_pPlayer->SetCameraOffset(D3DXVECTOR3(0.0f, 10.0f, 0.0f));
//            m_pPlayer->SetCameraLag(0.0f); 
//            m_pPlayer->SetCameraMode(FIRST_PERSON_CAMERA);
//            m_pCamera = m_pPlayer->GetCamera();
//            break;
//        case THIRD_PERSON_CAMERA:
//            m_pPlayer->SetFriction(250.0f); 
//            m_pPlayer->SetGravity(D3DXVECTOR3(0.0f, 0.0f, 0.0f));
//            m_pPlayer->SetMaxVelocityXZ(80.0f);
//            m_pPlayer->SetMaxVelocityY(400.0f);
//            m_pPlayer->SetCameraOffset(D3DXVECTOR3(0.0f, 10.0f, -40.0f));
//            m_pPlayer->SetCameraLag(0.25f); 
//            m_pPlayer->SetCameraMode(THIRD_PERSON_CAMERA);
//            m_pCamera = m_pPlayer->GetCamera();
//            break;
//        case SPACESHIP_CAMERA:            
//            m_pPlayer->SetFriction(125.0f); 
//            m_pPlayer->SetGravity(D3DXVECTOR3(0, 0, 0));
//            m_pPlayer->SetMaxVelocityXZ(80.0f);
//            m_pPlayer->SetMaxVelocityY(400.0f);
//            m_pPlayer->SetCameraOffset(D3DXVECTOR3(0.0f, 0.0f, 0.0f));
//            m_pPlayer->SetCameraLag(0.0f); 
//            m_pPlayer->SetCameraMode(SPACESHIP_CAMERA);
//            m_pCamera = m_pPlayer->GetCamera();
//            break;  
//		default:
//			break;
//	}
//	m_pPlayer->Update(m_GameTimer.GetTimeElapsed());
//	m_pCamera->SetViewMatrixToDevice(m_pD3DDevice);
//	if (m_pCamera && m_pScene) m_pScene->SetCamera(m_pCamera);
//}

bool CGameFramework::BuildObjects()
{
    ReleaseObjects();

	srand(timeGetTime());

	m_pScene = (CScene*)new object_manager();
	m_pScene->SetD3DDevice(m_pD3DDevice);
	m_pScene->BuildObjects();

	return(true);
}

void CGameFramework::ReleaseObjects()
{
	if (m_pScene) m_pScene->ReleaseObjects();
	if (m_pScene) delete m_pScene;
	m_pScene = NULL;
}

void CGameFramework::OnDestroy()
{
	ReleaseObjects();

	if (m_pD3D) m_pD3D->Release();
	if (m_pD3DDevice) m_pD3DDevice->Release();

    if (m_hMenu) DestroyMenu(m_hMenu);
    m_hMenu = NULL;

    SetMenu(m_hWnd, NULL);
    if (m_hWnd) DestroyWindow(m_hWnd);
    m_hWnd = NULL;
}

void CGameFramework::OnProcessingMouseMessage(HWND hWnd, UINT nMessageID, WPARAM wParam, LPARAM lParam)
{
	switch (nMessageID)
	{
        case WM_LBUTTONDOWN:			
            SetCapture(hWnd);
			GetCursorPos(&m_ptOldCursorPos);
            break;
        case WM_RBUTTONDOWN:			
            SetCapture(hWnd);
			GetCursorPos(&m_ptOldCursorPos);
            break;
        case WM_MOUSEMOVE:
			break;
        case WM_LBUTTONUP:
            ReleaseCapture();
			break;
        case WM_RBUTTONUP:
            ReleaseCapture();
			if (m_pScene) m_pScene->m_nRotatingObject = -1;
			break;
		default:
			break;
	}
}

void CGameFramework::OnProcessingKeyboardMessage(HWND hWnd, UINT nMessageID, WPARAM wParam, LPARAM lParam)
{
	switch (nMessageID)
	{
        case WM_KEYDOWN:
			switch (wParam) 
            {
				case VK_ESCAPE:
					::PostQuitMessage(0);
                case VK_RETURN:
					if (::GetKeyState(VK_SHIFT) & 0xFF00) ResetDisplay(true, 0, 0);
                    break;                                    
				case VK_F1:
					m_pScene->OnChangeCamera(THIRD_PERSON_CAMERA, m_GameTimer.GetTimeElapsed());
					break;
				case VK_F2:
					m_pScene->OnChangeCamera(SPACESHIP_CAMERA, m_GameTimer.GetTimeElapsed());
					break;
                case VK_F3:
					m_pScene->OnChangeCamera(SPACESHIP_TO_CENTER, m_GameTimer.GetTimeElapsed());
                    break;

				case VK_F5:
					m_pScene->OnChangeCamera(THIRD_PERSON_CAMERA2, m_GameTimer.GetTimeElapsed());					
					break;

				case VK_SPACE:
					break;
				default:
					break;
			} 
		default:
			break;
	}
}

LRESULT CGameFramework::OnProcessingWindowMessage(HWND hWnd, UINT nMessageID, WPARAM wParam, LPARAM lParam)
{
	switch (nMessageID)
	{
        case WM_SIZE:
            if (wParam == SIZE_MINIMIZED)
            {
                SetActive(false);            
            } 
            else
            {
                SetActive(true);
				ResetDisplay(false, LOWORD(lParam), HIWORD(lParam));
            } 
			break;
        case WM_LBUTTONDOWN:
        case WM_LBUTTONUP:
        case WM_RBUTTONDOWN:
        case WM_RBUTTONUP:
        case WM_MOUSEMOVE:
			OnProcessingMouseMessage(hWnd, nMessageID, wParam, lParam);
            break;
        case WM_KEYDOWN:
        case WM_KEYUP:
			OnProcessingKeyboardMessage(hWnd, nMessageID, wParam, lParam);
			break;
	}
	return 0;
}

void CGameFramework::ProcessInput()
{	
	if(GetFocus() == m_hWnd) {
		static UCHAR pKeyBuffer[256];
		DWORD dwDirection = 0;
		if (GetKeyboardState(pKeyBuffer))
		{
			if (pKeyBuffer['W'] & 0xF0) dwDirection |= DIR_FORWARD;
			if (pKeyBuffer['S'] & 0xF0) dwDirection |= DIR_BACKWARD;
			if (pKeyBuffer['A'] & 0xF0) dwDirection |= CUV_LEFT;
			if (pKeyBuffer['D'] & 0xF0) dwDirection |= CUV_RIGHT;

			
			
			//if (pKeyBuffer[VK_PRIOR] & 0xF0) dwDirection |= DIR_UP;
			//if (pKeyBuffer[VK_NEXT] & 0xF0) dwDirection |= DIR_DOWN;		
	

			if(m_pScene->GetControlPlayerIndex() == 0) {
				if (pKeyBuffer['E'] & 0xF0) 				
					m_pScene->GetPlayer(0)->SetFloat( min(300.0f, m_pScene->GetPlayer(0)->GetFloat() + 5.0f)) ;
				if (pKeyBuffer['C'] & 0xF0)
					m_pScene->GetPlayer(0)->SetFloat( max(100.0f, m_pScene->GetPlayer(0)->GetFloat() - 5.0f)) ;
			}
			else {
				if (pKeyBuffer[VK_LEFT] & 0xF0) dwDirection |= DIR_LEFT;
				if (pKeyBuffer[VK_RIGHT] & 0xF0) dwDirection |= DIR_RIGHT;
			}
		}    
		float cxDelta = 0.0f, cyDelta = 0.0f;
		if (GetCapture() == m_hWnd)
		{
			SetCursor(NULL);
			POINT ptCursorPos;
			GetCursorPos(&ptCursorPos);
			cxDelta = (float)(ptCursorPos.x - m_ptOldCursorPos.x) / 3.0f;
			cyDelta = (float)(ptCursorPos.y - m_ptOldCursorPos.y) / 3.0f;
			SetCursorPos(m_ptOldCursorPos.x, m_ptOldCursorPos.y);
		} 
		if ((dwDirection != 0) || (cxDelta != 0.0f) || (cyDelta != 0.0f))
		{
			if (cxDelta || cyDelta) 
			{
				if(m_pScene->GetControlPlayerIndex() != 0) {					
						m_pScene->GetPlayer()->Rotate(cyDelta, cxDelta, 0.0f);        				
				}
			} 
			if (dwDirection) 
			{		
				m_pScene->GetPlayer()->UpdateVelocity(dwDirection, 500.0f * m_GameTimer.GetTimeElapsed());				
					
				if(m_pScene->GetControlPlayerIndex() == 0) {
					if (dwDirection & CUV_LEFT)
						m_pScene->GetPlayer()->Rotate(0.0f, -1.0f, 0.0f); 
					if (dwDirection & CUV_RIGHT)
						m_pScene->GetPlayer()->Rotate(0.0f, 1.0f, 0.0f);						
				}
			} 		
		} 	
		//m_pScene->GetPlayer()->Update(m_GameTimer.GetTimeElapsed());
		//m_pScene->GetCamera()->SetViewMatrixToDevice(m_pD3DDevice);
	}
}

void CGameFramework::AnimateObjects()
{
	float fTimeElapsed = m_GameTimer.GetTimeElapsed();
	if (m_pScene) m_pScene->AnimateObjects(fTimeElapsed);
}

void CGameFramework::FrameAdvance()
{    
	m_GameTimer.Tick();

	if (!m_bActive || !m_pD3DDevice) return;
    if (m_bLostDevice)
    {
        HRESULT hResult = m_pD3DDevice->TestCooperativeLevel();
        if (hResult == D3DERR_DEVICENOTRESET)
        {
            ResetDisplay(false, 0, 0);
            m_bLostDevice = false;
        } 
        else
        {
            return;
        } 
    } 

	ProcessInput();

	AnimateObjects();

    m_pD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0,0,255), 1.0f, 0);

	if (SUCCEEDED(m_pD3DDevice->BeginScene()))
	{
		if (m_pScene) m_pScene->Render();
		//m_pPlayer->Render(m_pD3DDevice);
        m_pD3DDevice->EndScene();
	}

    if (FAILED(m_pD3DDevice->Present(NULL, NULL, NULL, NULL))) m_bLostDevice = true;
}


